﻿using System;
using System.Threading;

namespace FIFP.Common
{
    /// <summary>
    /// Implements a basic IDispatcher object using a proxy SynchronizationContext object.
    /// </summary>
    public class BasicDispatcher : IDispatcher
    {
        #region Fields

        private SynchronizationContext _context;
        private int _threadId;

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BasicDispatcher"/> class
        /// for the current thread.
        /// </summary>
        public BasicDispatcher()
        {
            _threadId = Thread.CurrentThread.ManagedThreadId;
            _context = SynchronizationContext.Current;
        }

        #endregion Constructors

        #region Properties

        /// <summary>
        /// Gets a value indicating whether the caller is on a different thread to the one
        /// this object represents, and therefore must use an invoke method to make the
        /// required call.
        /// </summary>
        /// <value>
        /// true if the caller in on a different thread than the thread this object
        /// represents; otherwise, false.
        /// </value>
        public bool InvokeRequired
        {
            get { return _threadId != Thread.CurrentThread.ManagedThreadId; }
        }

        #endregion Properties

        #region Public Methods

        /// <summary>
        /// Invokes the specified action asynchronously on the thread this object represents.
        /// </summary>
        /// <param name="action">The action.</param>
        public void BeginInvoke(Action action)
        {
            if (!InvokeRequired || _context == null)
            {
                action();
            }
            else if (_context != null)
            {
                _context.Post(delegate { action(); }, null);
            }
        }

        /// <summary>
        /// Invokes the specified action asynchronously on the thread this object represents.
        /// </summary>
        /// <typeparam name="T">The parameter type.</typeparam>
        /// <param name="action">The action.</param>
        /// <param name="arg">The parameter.</param>
        public void BeginInvoke<T>(Action<T> action, T arg)
        {
            if (!InvokeRequired || _context == null)
            {
                action(arg);
            }
            else if (_context != null)
            {
                _context.Post(delegate { action(arg); }, null);
            }
        }

        /// <summary>
        /// Invokes the specified action asynchronously on the thread this object represents.
        /// </summary>
        /// <typeparam name="T1">The type of the first arg.</typeparam>
        /// <typeparam name="T2">The type of the second arg.</typeparam>
        /// <param name="action">The action.</param>
        /// <param name="arg1">The first arg.</param>
        /// <param name="arg2">The second arg.</param>
        public void BeginInvoke<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
        {
            if (!InvokeRequired || _context == null)
            {
                action(arg1, arg2);
            }
            else if (_context != null)
            {
                _context.Post(delegate { action(arg1, arg2); }, null);
            }
        }

        /// <summary>
        /// Invokes the specified action synchronously on the thread this object represents.
        /// </summary>
        /// <param name="action">The action.</param>
        public void Invoke(Action action)
        {
            if (!InvokeRequired || _context == null)
            {
                action();
            }
            else if (_context != null)
            {
                _context.Send(delegate { action(); }, null);
            }
        }

        /// <summary>
        /// Invokes the specified action synchronously on the thread this object represents.
        /// </summary>
        /// <typeparam name="T">The parameter type.</typeparam>
        /// <param name="action">The action.</param>
        /// <param name="arg">The parameter.</param>
        public void Invoke<T>(Action<T> action, T arg)
        {
            if (!InvokeRequired || _context == null)
            {
                action(arg);
            }
            else if (_context != null)
            {
                _context.Send(delegate { action(arg); }, null);
            }
        }

        /// <summary>
        /// Invokes the specified action synchronously on the thread this object represents.
        /// </summary>
        /// <typeparam name="T1">The type of the first arg.</typeparam>
        /// <typeparam name="T2">The type of the second arg.</typeparam>
        /// <param name="action">The action.</param>
        /// <param name="arg1">The first arg.</param>
        /// <param name="arg2">The second arg.</param>
        public void Invoke<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
        {
            if (!InvokeRequired || _context == null)
            {
                action(arg1, arg2);
            }
            else if (_context != null)
            {
                _context.Send(delegate { action(arg1, arg2); }, null);
            }
        }

        #endregion Public Methods
    }
}